package org.feng.navigation.adapter.aspect;

import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.feng.navigation.client.dto.Check;
import org.feng.navigation.client.dto.ErrorEnum;
import org.feng.navigation.client.dto.Response;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Objects;

/**
 * ApiDTO 参数切面：对ApiDTO进行校验，并输出请求和api执行结果
 *
 * @version V1.0
 * @author: junzi
 * @date: 2023年01月11日 19时39分
 */
@Slf4j
@Aspect
@Order(10)
@Component
public class ApiDtoMethodParamAspect {

    @Pointcut("execution(public * org.feng.navigation.adapter.controller.*.*(..))")
    private void apiMethod() {
    }

    @Around(value = "apiMethod()")
    public Object around(ProceedingJoinPoint joinPoint) {
        // 获取API方法参数
        final Object[] args = joinPoint.getArgs();
        // 获得方法签名
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();

        String methodDesc = null;
        if (method.isAnnotationPresent(ApiOperation.class)) {
            ApiOperation apiOperation = method.getAnnotation(ApiOperation.class);
            // 方法描述
            methodDesc = apiOperation.value() + ":" + method.getName();
        }
        if (Objects.isNull(methodDesc)) {
            methodDesc = method.getName();
        }

        String argsAsString = Arrays.toString(args);
        log.info("执行API [{}] 的请求参数：{}", methodDesc, argsAsString);
        Object proceedResult;
        try {
            try {
                checkParams(args);
            } catch (IllegalArgumentException e) {
                String errorMessage = e.getMessage();
                log.error("DTO 参数校验错误：{}", errorMessage);
                return Response.buildFailure(ErrorEnum.METHOD_ARGUMENTS_VALID_ERROR, errorMessage);
            }
            proceedResult = joinPoint.proceed(args);
        } catch (Throwable e) {
            log.error(ErrorEnum.APPLICATION_ERROR.getErrorMessage(), e);
            return Response.buildFailure(ErrorEnum.APPLICATION_ERROR, e.getMessage());
        }
        log.info("执行API [{}] 的结果：{}", method.getName(), proceedResult);
        return proceedResult;
    }

    private void checkParams(Object[] args) throws IllegalArgumentException {
        log.info("ApiDTO 参数校验");
        if (Objects.isNull(args)) {
            return;
        }

        for (Object arg : args) {
            if (arg instanceof Check) {
                Check dto = (Check) arg;
                dto.check();
            }
        }
    }
}
